#include "configdata.h"
#include <typeinfo>
#include <string>
#include <sstream>
#include <iostream>

// assert() for debugging. disable by defining NDEBUG.
#include <cassert>

string yamlchar_to_string(const yaml_char_t* data, size_t length) {
  char *ch_value = new char[length+1];
  strncpy(ch_value, (const char*)data, length);
  ch_value[length] = '\0';
  
  string value(ch_value);
  delete [] ch_value;
  return value;
}

BadConfigException::BadConfigException(char* reason) throw() {
  this->reason = reason;
}
BadConfigException::BadConfigException(string reason) throw() {
  this->reason = reason;
}
BadConfigException::~BadConfigException() throw() {}
const char* BadConfigException::what() const throw() {
  return this->reason.c_str();
}

VirtualItem::VirtualItem() {}
VirtualItem::~VirtualItem() {}

/*** CONFIG ITEM ***/
// See the header for an explanation of what's going on here.

ConfigItem::ConfigItem() {}
ConfigItem::~ConfigItem() {}
bool ConfigItem::checkCompleteness() {
  return false;
}
void ConfigItem::te(char* prefix, const char* fn) throw(BadConfigException) {
  string msg(prefix);
  msg += " (";
  msg += fn;
  msg += " called on ";
  msg += typeid(*this).name();
  msg += ")";
  throw BadConfigException(msg);
}
VirtualItem* ConfigItem::getMapping() throw(BadConfigException) {
  this->te("Unexpected mapping found", "getMapping()"); return NULL;
}
VirtualItem* ConfigItem::getSequence() throw(BadConfigException) {
  this->te("Unexpected sequence found", "getSequence()"); return NULL;
}
void ConfigItem::setNextItem(VirtualItem* next) throw(BadConfigException) {
  string msg = "setNextItem(";
  msg += typeid(*next).name();
  msg += ")";
  
  this->te("Internal error.", msg.c_str());
}
void ConfigItem::setNextItem(yaml_char_t* data, size_t length)
                                              throw(BadConfigException) {
  this->te("Unexpected character data.", "setNextItem(yaml,len)");
}

/*** BASE ITEM. Starting. Produces root. ***/

BaseItem::BaseItem() {}
BaseItem::~BaseItem() {}
VirtualItem* BaseItem::getMapping() throw(BadConfigException) {
  return new RootMapping();
}
bool BaseItem::checkCompleteness() { return true; }
void BaseItem::setNextItem(VirtualItem* next) throw(BadConfigException) {
  // sanity check
  RootMapping* rm = dynamic_cast<RootMapping*>(next);
  if (rm == NULL)
    throw BadConfigException("Internal error. BaseItem got a non-RootMapping.");
}

/*** ROOT ***/

RootMapping::RootMapping() {
  this->expectedSection = ANY;
}
RootMapping::~RootMapping() {}
VirtualItem* RootMapping::getSequence() throw(BadConfigException) {
  if (this->expectedSection == ANY)
    throw BadConfigException("Top section not declared");
  
  switch(this->expectedSection) {
    case EFFECTS:
      return new EffectsSequence();
      break;
    case MENUS:
      return new MenusSequence();
      break;
    case CURSORS:
      return new CursorsSequence();
      break;
    default:
      throw BadConfigException("Mapping declaration for unknown section");
      break;
  }
}
void RootMapping::setNextItem(yaml_char_t* data, size_t length)
                                                  throw(BadConfigException) {
  if (this->expectedSection != ANY)
    throw BadConfigException("Expecting a mapping section");
  
  string value = yamlchar_to_string(data, length);
  
  if (value == "effects") {
    this->expectedSection = EFFECTS;
  } else if (value == "menu items") {
    this->expectedSection = MENUS;
  } else if (value == "cursors") {
    this->expectedSection = CURSORS;
  } else {
    throw BadConfigException(string("Allowed first level keys are ") +\
            string("effects, menu items, cursors"));
  }
}
void RootMapping::setNextItem(VirtualItem* next) throw(BadConfigException) {
  if(this->expectedSection == EFFECTS) {
    EffectsSequence* es = dynamic_cast<EffectsSequence*>(next);
    if (es == NULL)
      throw BadConfigException("Expecting an effects section");
    this->data[string("effects")] = es;
  } else if (this->expectedSection == MENUS) {
    MenusSequence* ms = dynamic_cast<MenusSequence*>(next);
    if (ms == NULL)
      throw BadConfigException("Expecting a menu items section");
    this->data[string("menu items")] = ms;
  } else if (this->expectedSection == CURSORS) {
    CursorsSequence* cs = dynamic_cast<CursorsSequence*>(next);
    if (cs == NULL)
      throw BadConfigException("Expecting a cursors section");
    this->data[string("cursors")] = cs;
  } else {
    throw BadConfigException("Unexpected root mapping");
  }
  this->expectedSection = ANY;
}
bool RootMapping::checkCompleteness() {
  return this->data.count(string("effects")) == 1;
}

void RootMapping::createObj(configdata* cd) {
  // do the other sequence here first, so that they are available to
  // the effectssequence's menu/gestures actions.
  CursorsSequence* cs = dynamic_cast<CursorsSequence*>(this->data["cursors"]);
  if (cs)
    cs->createObj(cd);
  
  MenusSequence* ms = dynamic_cast<MenusSequence*>(this->data["menu items"]);
  if (ms)
    ms->createObj(cd);
  
  EffectsSequence* es = dynamic_cast<EffectsSequence*>(this->data["effects"]);
  if (es)
    es->createObj(cd);
}



/*** CURSORS ***/

CursorsSequence::CursorsSequence() {}
CursorsSequence::~CursorsSequence() {}
VirtualItem* CursorsSequence::getMapping() throw(BadConfigException) {
  return new CursorMapping();
}
bool CursorsSequence::checkCompleteness() { return true; }
void CursorsSequence::setNextItem(yaml_char_t* data, size_t length)
                                            throw(BadConfigException) {
  throw BadConfigException("Expecting cursors definition mapping");
}
void CursorsSequence::setNextItem(VirtualItem* next) throw(BadConfigException) {
  CursorMapping* cm = dynamic_cast<CursorMapping*>(next);
  if (cm != NULL) {
    this->data.push_back(cm);
  } else {
    string msg = "Internal error. Passing a non-CursorMapping ";
    msg += "type to CursorsSequence::setNextItem";
    throw BadConfigException(msg);
  }
}
void CursorsSequence::createObj(configdata* cd) {
  vector<CursorMapping*>::iterator it;
  for (it = this->data.begin(); it < this->data.end(); it++) {
    CursorMapping* cm = *it;
    cm->createObj(cd->cursors);
  }
}

CursorMapping::CursorMapping() {
  this->expectingKey = true;
  this->createdSequence = false;
}
CursorMapping::~CursorMapping() {}
void CursorMapping::setNextItem(VirtualItem* next)
                                                throw(BadConfigException) {
  CursorItems* cinext = dynamic_cast<CursorItems*>(next);
  if (cinext != NULL) {
    if (this->data.second == NULL)
      this->data.second = cinext;
    else
      // this one should not be reachable due to getSequence() below
      throw BadConfigException("Already declared a list of cursors");
  }
}
void CursorMapping::setNextItem(yaml_char_t* data, size_t length)
                                                throw(BadConfigException) {
  string value = yamlchar_to_string(data, length);
  
  if (this->expectingKey) {
    this->lastKey = value;
  } else {
    if (this->lastKey == "name")
      this->data.first = value;
    else
      throw BadConfigException("Unexpected value for key " + this->lastKey);
  }
  
  this->expectingKey = this->expectingKey ? false : true;
}
VirtualItem* CursorMapping::getSequence() throw(BadConfigException) {
  if (trim(this->lastKey) == "" || this->lastKey != "items")
    throw BadConfigException("'items' section not defined");
  else if (this->createdSequence)
    throw BadConfigException("Already created an items sequence for a cursor" +\
            string("item"));
  
  this->createdSequence = true;
  return new CursorItems();
}
bool CursorMapping::checkCompleteness() {
  return this->data.first != "" && this->data.second != NULL;
}
void CursorMapping::createObj(cursor_map& cm) {
  vector<string>* items = this->data.second->createObj();
  cm[this->data.first] = *items;
}

CursorItems::CursorItems() {}
CursorItems::~CursorItems() {}
void CursorItems::setNextItem(yaml_char_t* data, size_t length)
                                                throw(BadConfigException) {
  string item = yamlchar_to_string(data, length);
  
  this->data.push_back(item);
}
bool CursorItems::checkCompleteness() { return this->data.size() > 0; }
vector<string>* CursorItems::createObj() {
  vector<string>* ret = new vector<string>;
  vector<string>::iterator it;
  for (it = this->data.begin(); it != this->data.end(); it++)
    ret->push_back(*it);
  return ret;
}


/*** MENUS ***/

MenusSequence::MenusSequence() {}
MenusSequence::~MenusSequence() {}
VirtualItem* MenusSequence::getMapping() throw(BadConfigException) {
  return new MenuMapping();
}
bool MenusSequence::checkCompleteness() { return true; }
void MenusSequence::setNextItem(yaml_char_t* data, size_t length)
                                                  throw(BadConfigException) {
  throw BadConfigException("Expecting menus definition mapping");
}
void MenusSequence::setNextItem(VirtualItem* next) throw(BadConfigException) {
  MenuMapping* mm = dynamic_cast<MenuMapping*>(next);
  if (mm != NULL) {
    this->data.push_back(mm);
  } else {
    throw BadConfigException(string("Internal error. Passing a ") +\
            "non-MenuMapping type to MenusSequence::setNextItem");
  }
}
void MenusSequence::createObj(configdata* cd) {
  vector<MenuMapping*>::iterator it;
  for (it = this->data.begin(); it < this->data.end(); it++) {
    MenuMapping* mm = *it;
    mm->createObj(cd->menus);
  }
}

MenuMapping::MenuMapping() {
  this->expectingKey = true;
  this->createdSequence = false;
}
MenuMapping::~MenuMapping() {}
void MenuMapping::setNextItem(VirtualItem* next) throw(BadConfigException) {
  MenuItems* minext = dynamic_cast<MenuItems*>(next);
  if (minext != NULL) {
    if (this->data.second == NULL)
      this->data.second = minext;
    else
      // this one should not be reachable due to getSequence() below
      throw BadConfigException("Already declared a list of menu items");
  }
}
void MenuMapping::setNextItem(yaml_char_t* data, size_t length)
                                                throw(BadConfigException) {
  string value = yamlchar_to_string(data, length);
  
  if (this->expectingKey) {
    this->lastKey = value;
  } else {
    if (this->lastKey == "name")
      this->data.first = value;
    else
      throw BadConfigException("Unexpected value for key " + this->lastKey);
  }
  
  this->expectingKey = this->expectingKey ? false : true;
}
VirtualItem* MenuMapping::getSequence() throw(BadConfigException) {
  if (trim(this->lastKey) == "" || this->lastKey != "items")
    throw BadConfigException("'items' section not defined");
  else if (this->createdSequence)
    throw BadConfigException("Already created an items sequence for a menu " +\
            string("item"));
  
  this->createdSequence = true;
  return new MenuItems();
}
bool MenuMapping::checkCompleteness() {
  return this->data.first != "" && this->data.second != NULL;
}
void MenuMapping::createObj(menu_map& mm) {
  mm[this->data.first] = *(this->data.second->createObj());
}

MenuItems::MenuItems() {}
MenuItems::~MenuItems() {}
VirtualItem* MenuItems::getMapping() throw(BadConfigException) {
  return new MenuItemMapping();
}
void MenuItems::setNextItem(VirtualItem* next) throw(BadConfigException) {
  MenuItemMapping* mim = dynamic_cast<MenuItemMapping*>(next);
  if (mim != NULL)
    this->data.push_back(mim);
  else
    throw BadConfigException("The list of menu items contains a " +\
            string("non-MenuItemMapping item"));
}
bool MenuItems::checkCompleteness() {
  return this->data.size() > 0;
}
vector<menu_item*>* MenuItems::createObj() {
  vector<menu_item*>* ret = new vector<menu_item*>;
  for (unsigned int i = 0; i < this->data.size(); i++)
    ret->push_back(this->data[i]->createObj());
  return ret;
}

MenuItemMapping::MenuItemMapping() {
  this->expectingLabel = false;
  this->expectingAction = false;
}
MenuItemMapping::~MenuItemMapping() {}
void MenuItemMapping::setNextItem(yaml_char_t* data, size_t length)
                                                  throw(BadConfigException) {
  string value = yamlchar_to_string(data, length);
  
  if (this->data.second != NULL) { // we're adding to the action
    this->data.second->setNextItem(&value);
  } else if (this->expectingAction) { // this is the action name.
                                      // create the actionitem object
    this->expectingAction = false;
    this->data.second = createNewActionItem(value);
  } else if (this->expectingLabel) { // this is the label
    this->data.first = value;
    this->expectingLabel = false;
  } else if (value == "label") { // next value should be the label
    this->expectingLabel = true;
  } else if (value == "action") { // next value should be the action
    this->expectingAction = true;
  } else {
    throw BadConfigException("Incorrect ordering of elements");
  }
}
bool MenuItemMapping::checkCompleteness() {
  return this->data.first != "" && this->data.second != NULL;
}
menu_item* MenuItemMapping::createObj() {
  menu_item *mi = new menu_item;
  char* label = str2char(this->data.first);
  mi->label = label;
  mi->action = this->data.second->createObj();
  return mi;
}


/*** EFFECTS ***/

EffectsSequence::EffectsSequence() {}
EffectsSequence::~EffectsSequence() {}
VirtualItem* EffectsSequence::getMapping() throw(BadConfigException) {
  return new TriggerActionMapping();
}
bool EffectsSequence::checkCompleteness() {
  return true;
}
void EffectsSequence::setNextItem(yaml_char_t* data, size_t length)
                                              throw(BadConfigException) {
  throw BadConfigException("Expecting effects definition mapping");
}
void EffectsSequence::setNextItem(VirtualItem* next) throw(BadConfigException) {
  TriggerActionMapping* tam = dynamic_cast<TriggerActionMapping*>(next);
  if (tam != NULL) {
    this->data.push_back(tam);
  } else {
    string msg = "Internal error. Passing a non-TriggerActionMapping ";
    msg += "type to EffectsSequence::setNextItem";
    throw BadConfigException(msg);
  }
}
void EffectsSequence::createObj(configdata* cd) {
  for (unsigned int i = 0; i < this->data.size(); i++) {
    TriggerActionMapping* tam = this->data[i];
    effect* ef = (effect*)malloc(sizeof(effect));
    tam->createObj(*ef, cd);
    cd->effects.push_back(ef);
  }
}

/*** Trigger + Action Mapping ***/

TriggerActionMapping::TriggerActionMapping() {
  this->alreadyCreatedMapping = false;
  this->expectingAction = false;
}
TriggerActionMapping::~TriggerActionMapping() {}
VirtualItem* TriggerActionMapping::getMapping() throw(BadConfigException) {
  if (this->alreadyCreatedMapping)
    throw BadConfigException("Already declared a trigger mapping");
  
  this->alreadyCreatedMapping = true;
  return new TriggerMapping();
}
void TriggerActionMapping::setNextItem(VirtualItem* next)
                                              throw(BadConfigException) {
  TriggerMapping* tmnext = dynamic_cast<TriggerMapping*>(next);
  if (tmnext != NULL) {
    if (this->data.first == NULL)
      this->data.first = tmnext;
    else
      // this one should not be reachable due to getMapping() above
      throw BadConfigException("Already declared a trigger mapping");
  }
}
void TriggerActionMapping::setNextItem(yaml_char_t* data, size_t length)
                                              throw(BadConfigException) {
  string value = yamlchar_to_string(data, length);
  
  if (this->data.second != NULL) // we're adding to the action
    this->data.second->setNextItem(&value);
  else if (this->expectingAction) { // create the actionitem object
    this->expectingAction = false;
    this->data.second = createNewActionItem(value);
  } else if (value == "trigger") {
    return; // skip triggers. they get handled in getMapping/setNextItem(TM)
  } else if (value == "action") { // next value should be the action
    this->expectingAction = true;
  } else {
    throw BadConfigException("Incorrect ordering of elements");
  }
    
}
bool TriggerActionMapping::checkCompleteness() {
  return this->data.first != NULL && this->data.second != NULL
          && this->data.second->checkCompleteness();
}
void TriggerActionMapping::createObj(effect& e, configdata* cd) {
  Trigger* t = this->data.first->createObj();
  Action* a;
  
  MenuActionItem* mai = dynamic_cast<MenuActionItem*>(this->data.second);
  GesturesActionItem* gai =
          dynamic_cast<GesturesActionItem*>(this->data.second);
  if (mai != NULL)
    a = mai->createObj(cd);
  else if (gai != NULL)
    a = gai->createObj(cd);
  else
    a = this->data.second->createObj();
  e.t = t;
  e.a = a;
}

/*** Trigger Mapping ***/

TriggerMapping::TriggerMapping() {
  this->expectingKey = true;
}
TriggerMapping::~TriggerMapping() {}
void TriggerMapping::setNextItem(yaml_char_t* data, size_t length)
                                            throw(BadConfigException) {
  string value = yamlchar_to_string(data, length);
  
  if (this->expectingKey) {
    this->data[value] = string();
    this->lastKey = value;
  } else
    this->data[this->lastKey] = value;
  
  this->expectingKey = this->expectingKey ? false : true;
}
bool TriggerMapping::checkCompleteness() {
  return this->data.count("button") == 1 &&
          this->data.count("edge") == 1;
}
Trigger* TriggerMapping::createObj() throw(BadConfigException) {
  unsigned int btn = str2int(this->data["button"]);
  string stredge = this->data["edge"];
  const Place* edge = str2place(stredge);
  
  Trigger* t;
  if (this->data.count("class") == 1){
    char* cls = str2char(this->data["class"].c_str());
    t = new Trigger(btn, *edge, cls);
  } else
    t = new Trigger(btn, *edge, NULL);
  
  return t;
}

/*** ACTIONS ***/

ActionItem::ActionItem() {}
ActionItem::~ActionItem() {}

StatefulKeyActionItem::StatefulKeyActionItem() { this->expectingKey = true; }
StatefulKeyActionItem::~StatefulKeyActionItem() {}
void StatefulKeyActionItem::setNextItem(string* data)
                                                  throw(BadConfigException) {
  if (this->expectingKey) {
    this->data[*data] = string();
    this->lastKey = *data;
  } else
    this->data[this->lastKey] = *data;
  
  this->expectingKey = this->expectingKey ? false : true;
}
bool StatefulKeyActionItem::checkCompleteness() {
  return this->data.count("modifier") == 1 &&
          this->data.count("button1") == 1 &&
          this->data.count("key1") == 1 &&
          this->data.count("button2") == 1 &&
          this->data.count("key2") == 1;
}
Action* StatefulKeyActionItem::createObj() throw(BadConfigException) {
  // check that the specified key mappings exist
  KeySym mod = str2keysym(this->data["modifier"].c_str(),
          "stateful key modifier");
  
  unsigned int b1 = str2button(this->data["button1"].c_str());
  unsigned int b2 = str2button(this->data["button2"].c_str());
  
  KeySym k1 = str2keysym(this->data["key1"].c_str(), "stateful key1");
  KeySym k2 = str2keysym(this->data["key2"].c_str(), "stateful key2");
  
  return new Stateful_Key(mod, b1, k1, b2, k2);
}

KeyActionItem::KeyActionItem() { this->expectingKey = true; }
KeyActionItem::~KeyActionItem() {}
void KeyActionItem::setNextItem(string* data) throw(BadConfigException) {
  if (this->expectingKey) {
    this->data[*data] = string();
    this->lastKey = *data;
  } else
    this->data[this->lastKey] = *data;
  
  this->expectingKey = this->expectingKey ? false : true;
}
bool KeyActionItem::checkCompleteness() {
  return this->data.count("key") == 1;
}
Action* KeyActionItem::createObj() throw(BadConfigException) {
  // check that the specified key mappings exist
  KeySym key = str2keysym(this->data["key"].c_str(),
          "key");
  
  KeySym mod1 = NoSymbol;
  KeySym mod2 = NoSymbol;
  if (this->data.count("modifier1") == 1) {
    mod1 = str2keysym(this->data["modifier1"].c_str(), "key modifier1");
  }
  if (this->data.count("modifier2") == 1) {
    mod2 = str2keysym(this->data["modifier2"].c_str(), "key modifier2");
  }
  if (this->data.count("edge") == 1) {
    const Place* p = str2place(this->data["edge"]);
    return new Key_Action(*p, mod1, mod2, key);
  } else {
    return new Key_Action(mod1, mod2, key);
  }
}

CommandActionItem::CommandActionItem() { this->expectingKey = true; }
CommandActionItem::~CommandActionItem() {}
void CommandActionItem::setNextItem(string* data) throw(BadConfigException) {
  if (this->expectingKey) {
    this->data[*data] = string();
    this->lastKey = *data;
  } else
    this->data[this->lastKey] = *data;
  
  this->expectingKey = this->expectingKey ? false : true;
}
bool CommandActionItem::checkCompleteness() {
  return this->data.count("command") == 1;
}
Action* CommandActionItem::createObj() throw(BadConfigException) {
  char *cmd = str2char(this->data["command"].c_str());
  return new System_Action(cmd);
}

MenuActionItem::MenuActionItem() { this->expectingKey = true; }
MenuActionItem::~MenuActionItem() {}
void MenuActionItem::setNextItem(string* data) throw(BadConfigException) {
  if (this->expectingKey) {
    this->data[*data] = string();
    this->lastKey = *data;
  } else
    this->data[this->lastKey] = *data;
  
  this->expectingKey = this->expectingKey ? false : true;
}
bool MenuActionItem::checkCompleteness() {
  return this->data.count("menu") == 1;
}
Action* MenuActionItem::createObj() throw(BadConfigException) {
  throw BadConfigException(string("Internal error. Config data not passed") +\
          " to MenuActionItem::createObj()");
}
Action* MenuActionItem::createObj(configdata* cd) throw(BadConfigException) {
  string menuName = this->data["menu"];
  
  // check if the menu exists
  if (cd->menus.count(menuName) == 0)
    throw BadConfigException("Menu " + menuName + " was not defined");
  
  // convert the vector into a flat array space
  menu_item* menu = flattenMenu(cd->menus[menuName]);
  
  return new Vertical_Menu(menu, cd->menus[menuName].size(), menuName.c_str());
}

GesturesActionItem::GesturesActionItem() { this->expectingKey = true; }
GesturesActionItem::~GesturesActionItem() {}
void GesturesActionItem::setNextItem(string* data) throw(BadConfigException) {
  if (this->expectingKey) {
    this->data[*data] = string();
    this->lastKey = *data;
  } else
    this->data[this->lastKey] = *data;
  
  this->expectingKey = this->expectingKey ? false : true;
}
bool GesturesActionItem::checkCompleteness() {
  return this->data.count("area") == 1 && this->data.count("menu") == 1
          && this->data.count("cursors") == 1;
}
Action* GesturesActionItem::createObj() throw(BadConfigException) {
  throw BadConfigException(string("Internal error. Config data not passed") +\
          " to GesturesActionItem::createObj()");
}
Action* GesturesActionItem::createObj(configdata* cd)
                                                    throw(BadConfigException) {
  const Place* area = str2place(this->data["area"]);
  string menuName = this->data["menu"];
  string cursorsName = this->data["cursors"];
  
  // check if the menu exists
  if (cd->menus.count(menuName) == 0)
    throw BadConfigException("Menu " + menuName + " was not defined");
  if (cd->cursors.count(cursorsName) == 0)
    throw BadConfigException("Cursor set " + cursorsName + " was not defined");
  
  // convert the vector into a flat array space
  menu_item* menu = flattenMenu(cd->menus[menuName]);
  return new Gesture_Menu(*area, menu,
          cd->menus[menuName].size(), cd->cursors[cursorsName],
          menuName.c_str(), cursorsName.c_str());
}

NoActionItem::NoActionItem() {}
NoActionItem::~NoActionItem() {}
void NoActionItem::setNextItem(string* data) throw(BadConfigException) {}
bool NoActionItem::checkCompleteness() {
  return true;
}
Action* NoActionItem::createObj() throw(BadConfigException) {
  return new No_Action();
}



/*** HELPERS ***/
ActionItem* createNewActionItem(string& value) {
  if (value == "stateful key") {
      return new StatefulKeyActionItem();
    } else if (value == "key") {
      return new KeyActionItem();
    } else if (value == "command") {
      return new CommandActionItem();
    } else if (value == "menu") {
      return new MenuActionItem();
    } else if (value == "gestures") {
      return new GesturesActionItem();
    } else if (value =="no action") {
      return new NoActionItem();
    } else {
      throw BadConfigException(string("Unknown action used: ") + \
              value);
    }
}

inline unsigned int str2int(const string& str) throw(BadConversion) {
  istringstream i(str);
  unsigned int x;
  if (!(i >> x))
    throw BadConversion(str);
  return x;
}

char* str2char(const string& str) {
  int length = str.size();
  char *ch_value = new char[length+1];
  strncpy(ch_value, str.c_str(), length);
  ch_value[length] = '\0';
  
  return ch_value;
}

KeySym str2keysym(const char* str, string kindOfAction)
                                                throw(BadConfigException) {
  KeySym ret = XStringToKeysym(str);
  if (ret == NoSymbol)
    throw BadConfigException(string("Got ") + str + " for a " +\
            kindOfAction + ". Keys must be legal symbols from keysymdef.h" +\
            " without the XK_ prefix (ie. XK_Alt_L becomes Alt_L).");
  return ret;
}

unsigned int str2button(const char* str) throw(BadConfigException) {
  unsigned int ret;
  try {
    ret = str2int(str);
  } catch (BadConversion& e) {
    throw BadConfigException(string("Got ") + e.what() + " as a mouse " +\
            "button value. It should be 1) an integer, and 2) one of " +\
            "the values listed in xmodmap -pp or xev. See docs.");
  }
  return ret;
}

const Place* str2place(string& stredge) {
  const Place* edge;
  if(stredge == "top")
    edge = &TOP;
  else if (stredge == "left")
    edge = &LEFT;
  else if (stredge == "right")
    edge = &RIGHT;
  else if (stredge == "bottom")
    edge = &BOTTOM;
  else if (stredge == "top left")
    edge = &TOP_LEFT;
  else if (stredge == "top right")
    edge = &TOP_RIGHT;
  else if (stredge == "bottom left")
    edge = &BOTTOM_LEFT;
  else if (stredge == "bottom right")
    edge = &BOTTOM_RIGHT;
  else
    throw BadConfigException("Edge name cannot be " + stredge +
            "Only one of the following is allowed: top, left, right," +
            " bottom, top left, top right, bottom left, bottom right");
  return edge;
}

// convert the vector into a flat array space
menu_item* flattenMenu(vector<menu_item*>& items) {
  size_t menu_item_size = sizeof(menu_item);
  menu_item* menus = (menu_item*)malloc(menu_item_size * items.size());
  for (unsigned int i = 0; i < items.size(); i++)
    menus[i] = *items[i];
  return menus;
}
Cursor* flattenCursors(vector<Cursor>& items) {
  size_t size = sizeof(Cursor);
  Cursor* cursors = (Cursor*)malloc(size * items.size());
  for (unsigned int i = 0; i < items.size(); i++)
    cursors[size*i] = items[i];
  return cursors;
}

inline string trim(const string& o) {
  string ret = o;
  ret.erase(ret.find_last_not_of("\n\t\v\f\r ")+1);
  ret.erase(0, ret.find_first_not_of("\n\t\v\f\r "));
  return ret;
}

